#define MAXLENGTH 1000

#include <windows.h>

#include "guid.h"
#include "cpnmouse_mylapi.h"

#include <stdio.h>
#include <stdlib.h>

#include <setupapi.h>

////////////////////////////////////////////////////////////////////////
// Modified by Pierre Dragicevic:
//
// We had to redefine lapi.c here because device names (devicepaths)
// were not accessible from outside.
//
// --> Also added code for retrieveing device key name from registry, in order
// to get the port it is attached to !
////////////////////////////////////////////////////////////////////////

typedef struct {
	HANDLE handle;
	HANDLE suspend;
	char *devicepath;
	char *driverkey;
	char *hardwareid;
} MouseData;

callback theCallback = NULL;
MouseData *mice = NULL;
int maxmouse = 1;
int maxused = 0;
HANDLE hMutex = NULL;

void __cdecl lRegisterCallback(callback c) {
	if (hMutex == NULL) {
		hMutex = CreateMutex(NULL, FALSE, NULL);
	}
	theCallback = c;
}

void __cdecl lUnRegisterCallback(void) {
	theCallback = NULL;
	CloseHandle(hMutex); hMutex = NULL;
}

void ApcCallback(PVOID NormalContext, PVOID SystemArgument1, PVOID SystemArgument2) {
	if (theCallback) {
		while (WaitForSingleObject(hMutex, INFINITE) != WAIT_OBJECT_0) ;
		theCallback((int) NormalContext,
				(signed int) (signed short int) ((((unsigned int) SystemArgument1) & 0xffff0000) >> 16),
				(signed int) (signed short int) (((unsigned int ) SystemArgument1) & 0xffff),
				(unsigned int) ((((unsigned int) SystemArgument2) & 0xffff0000) >> 16),
				(int) (((unsigned int) SystemArgument2) & 0xffff));
		ReleaseMutex(hMutex);
	}
}

int __cdecl lGetMice(int count) {
	int number = 0;
	HANDLE new = INVALID_HANDLE_VALUE;
	HDEVINFO hardwareDeviceInfo;
	SP_INTERFACE_DEVICE_DATA deviceInfoData;
	BOOLEAN done = FALSE;
	ULONG i = 0;

	PSP_INTERFACE_DEVICE_DETAIL_DATA functionClassDeviceData = NULL;
	ULONG predictedLength = 0;
	ULONG requiredLength = 0;
	
	// Added by Pierre
	char *buffer = (char *) malloc(600 * sizeof(char));
	DWORD intbuffer = -1;
	SP_DEVINFO_DATA devInfo;

	hardwareDeviceInfo = SetupDiGetClassDevs(
			(LPGUID) &GUID_CLASS_MOUSE_CPNTOOLS,
			NULL, // Define no enumerator (global)
			NULL, // Define no
			(DIGCF_PRESENT | // Only Devices present
			 DIGCF_DEVICEINTERFACE)); // Function class devices

	deviceInfoData.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
	devInfo.cbSize = sizeof(SP_DEVINFO_DATA);

///////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////

	while ((number < count) || (count == 0)) {
		if (SetupDiEnumDeviceInterfaces(
					hardwareDeviceInfo,
					0, // Don't care about specific PDOs
					(LPGUID) &GUID_CLASS_MOUSE_CPNTOOLS,
					i,
					&deviceInfoData)) {
			SetupDiGetInterfaceDeviceDetail(
					hardwareDeviceInfo,
					&deviceInfoData,
					NULL, // probe, no output buffer,
					0, // probe, output buffer of length 0
					&requiredLength,
					NULL); // not interested in specific dev-node

			functionClassDeviceData = (PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc(requiredLength);
			functionClassDeviceData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
			predictedLength = requiredLength;

			if (SetupDiGetInterfaceDeviceDetail(
						hardwareDeviceInfo,
						&deviceInfoData,
						functionClassDeviceData,
						predictedLength,
						&requiredLength,
						&devInfo)) {  	// Added by Pierre
				char c[MAXLENGTH];
				sprintf(c, "%s\\execute\\get\\%u\\%u", 
						functionClassDeviceData->DevicePath,
						maxused + 1,
						&ApcCallback);

				new = CreateFile(
						c,
						0,
						FILE_SHARE_READ | FILE_SHARE_WRITE, // Don't want to share access
						NULL, // no SECURITY_ATTRIBUTES structure
						OPEN_EXISTING, // No special create flags
						0, // No special attributes
						NULL); // No template file

				sprintf(c, "%s", functionClassDeviceData->DevicePath);

				if (new != INVALID_HANDLE_VALUE) {
					maxused++;
					number++;
					if (maxused >= maxmouse) {
						maxmouse *= 2;
						mice = (MouseData *) realloc(mice, maxmouse * sizeof(MouseData));
					}
					mice[maxused].handle = new;
					mice[maxused].suspend = NULL;
					// added by Pierre
					mice[maxused].devicepath = (char *) malloc((strlen(functionClassDeviceData->DevicePath) + 1) * sizeof(char));
					strcpy(mice[maxused].devicepath, functionClassDeviceData->DevicePath);
					//
					SetupDiGetDeviceRegistryPropertyA(hardwareDeviceInfo, &devInfo, SPDRP_DRIVER, NULL, buffer, 600, NULL);
					mice[maxused].driverkey = (char *) malloc((strlen(buffer) + 1) * sizeof(char));
					strcpy(mice[maxused].driverkey, buffer);
					//
					SetupDiGetDeviceRegistryPropertyA(hardwareDeviceInfo, &devInfo, SPDRP_HARDWAREID, NULL, buffer, 600, NULL);
					mice[maxused].hardwareid = (char *) malloc((strlen(buffer) + 1) * sizeof(char));
					strcpy(mice[maxused].hardwareid, buffer);
					//
					SetupDiGetDeviceRegistryPropertyA(hardwareDeviceInfo, &devInfo, 0x00000015, NULL, &intbuffer, 600, NULL);
//printf("BUSNUMBER: %d\n", intbuffer);
				}
			}

			free(functionClassDeviceData);
		} else if (ERROR_NO_MORE_ITEMS == GetLastError())
			break;
		i++;
	}

	SetupDiDestroyDeviceInfoList(hardwareDeviceInfo);
	return number;
}

int __cdecl lHasMouse(int number) {
	if ((number <= maxused) && (number > 0)) {
		return mice[number].handle != 0;
	}
	return 0;
}

void __cdecl lUnGetMouse(number) {
	if (lHasMouse(number)) {
		lUnSuspendMouse(number);
		CloseHandle(mice[number].handle);
		mice[number].handle = NULL;
		free(mice[number].devicepath);
		mice[number].devicepath = NULL;
		free(mice[number].driverkey);
		mice[number].driverkey = NULL;
		free(mice[number].hardwareid);
		mice[number].hardwareid = NULL;
		while ((maxused > 0) && (lHasMouse(maxused))) maxused--;
	}
}

void __cdecl lUnGetAllMice() {
	int i;
	for (i = 0; i < maxused; ++i)
		lUnGetMouse(i);
}

void __cdecl lSuspendMouse(number) {
	if (lHasMouse(number)) {
		if (mice[number].suspend == NULL) {
			char c[MAXLENGTH];
			sprintf(c, "%s\\execute\\suspend", mice[number].devicepath);
			mice[number].suspend = CreateFile(
					c, // Filename
					0, // Access
					FILE_SHARE_READ | FILE_SHARE_WRITE, // Share
					NULL, // No SECURITY_ATTRIBUTES
					OPEN_EXISTING,
					0, // No special attributes
					NULL); // No template file
		}
	}
}

void __cdecl lUnSuspendMouse(number) {
	if (lHasMouse(number)) {
		if (mice[number].suspend) {
			CloseHandle(mice[number].suspend);
			mice[number].suspend = NULL;
		}
	}
}

// This is the method we added to lapi.c for getting the device name
char* lGetDevicePath(number) {
	if (lHasMouse(number)) {
		return mice[number].devicepath;
	}
	return NULL;
}

// This is another method we added to get driver key name
char* lGetDriverKey(number) {
	if (lHasMouse(number)) {
		return mice[number].driverkey;
	}
	return NULL;
}

char* lGetHardwareId(number) {
	if (lHasMouse(number)) {
		return mice[number].hardwareid;
	}
	return NULL;
}